package com.atguigu.exam.service.impl;


import com.atguigu.exam.common.CacheConstants;
import com.atguigu.exam.common.Result;
import com.atguigu.exam.entity.*;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionPageVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Autowired
    private QuestionMapper questionMapper;


    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;
    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;
    @Autowired
    private PaperQuestionMapper paperQuestionMapper;
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 分页查询题目列表
     *
     * @param pageBean
     * @param questionPageVo
     */
    @Override
    public void getQuestionList(Page<Question> pageBean, QuestionPageVo questionPageVo) {
        //调用mapper方法
        questionMapper.selectQuestionAndAnswer(pageBean,  questionPageVo);
    }

    /**
     * 分页查询题目列表 方案二
     *
     * @param pageBean
     * @param questionPageVo
     */
    @Override
    public void getQuestionJavaList(Page<Question> pageBean, QuestionPageVo questionPageVo) {
        //1.分页查询动态参数列表
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //动态参数拼接
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getCategoryId()),
                Question::getCategoryId, questionPageVo.getCategoryId());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getType()),
                Question::getType, questionPageVo.getType());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getDifficulty()),
                Question::getDifficulty, questionPageVo.getDifficulty());
        lambdaQueryWrapper.like(!ObjectUtils.isEmpty(questionPageVo.getKeyword()),
                Question::getTitle, questionPageVo.getKeyword());
        //按照最新更新时间以及创建时间倒叙排序
        lambdaQueryWrapper.orderByDesc(Question::getUpdateTime);
        lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
        page(pageBean, lambdaQueryWrapper);

        //2.提取一个方法， 给题目进行选项和答案装填
        fillQuestionChoiceAndAnswer(pageBean.getRecords());

    }

    /**
     * 根据ID查询单个题目详情
     *
     * @param id
     * @return
     */
    @Override
    public Question getQuestionById(Long id) {
        //调用mapper进行查询
        Question question = questionMapper.selectQuestionById(id);
        if (question == null) {
            throw new RuntimeException("查询题目详情失败");
        }
        //进行热点题目次数保存
        new Thread(() -> incrementQuestion(question.getId())).start();
        return question;
    }

    /**
     * 创建新题目
     *
     * @param question
     */
    @Transactional(rollbackFor = Exception.class) //添加事务，涉及到多表修改
    @Override
    public void createQuestion(Question question) {
        //判断当前类下是否存在相同名称的题目
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //相同分类下不同id名称相同
        lambdaQueryWrapper.eq(Question::getTitle, question.getTitle());
        lambdaQueryWrapper.ne(Question::getId, question.getId());
        lambdaQueryWrapper.eq(Question::getCategoryId, question.getCategoryId());
        boolean exists = getBaseMapper().exists(lambdaQueryWrapper);

        //存在则抛出异常
        if (exists) {
            throw new RuntimeException("该分类下已存在相同名称的题目");
        }

        //1.先保存题目基本信息
        save(question);

        //2.判断是否是选择题
        //如果是选择题

        //获取答案对象
        QuestionAnswer questionAnswer = question.getAnswer();
        //为答案对象设置question_id
        questionAnswer.setQuestionId(question.getId());
        if ("CHOICE".equals(question.getType())) {
            //获取选项列表
            List<QuestionChoice> choices = question.getChoices();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choices.size(); i++) {
                //获取每个选项对象
                QuestionChoice questionChoice = choices.get(i);
                //为选项设置question_id
                questionChoice.setQuestionId(question.getId());
                //为选项设置排序
                questionChoice.setSort(i);
                //保存选项
                questionChoiceMapper.insert(questionChoice);
                //获取选择题的答案
                if (questionChoice.getIsCorrect()) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
            }

            String answer = sb.toString();
            //设置答案对象
            questionAnswer.setAnswer(answer);
        }

        //3.保存答案
        questionAnswerMapper.insert(questionAnswer);

    }

    /**
     * 更新题目信息
     *
     * @param id
     * @param question
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateQuestion(Long id, Question question) {
        //判断是否存在相同名称的题目
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Question::getTitle, question.getTitle());
        lambdaQueryWrapper.eq(Question::getCategoryId, question.getCategoryId());
        lambdaQueryWrapper.ne(Question::getId, question.getId());
        boolean exists = getBaseMapper().exists(lambdaQueryWrapper);
        if (exists) {
            throw new RuntimeException("存在与该题目相同内容的题目");
        }

        //设置更新时间为null 自动填充
        question.setUpdateTime(null);
        //更新题目基本信息
        boolean updated = updateById(question);
        if (!updated) {
            throw new RuntimeException("更新题目失败");
        }
        //获取答案选项
        QuestionAnswer questionAnswer = question.getAnswer();
        //删除原来的选项
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, question.getId()));
        //如果是选择题
        if ("CHOICE".equals(question.getType())) {
            StringBuilder sb = new StringBuilder();
            //获取选项对象
            List<QuestionChoice> choices = question.getChoices();
            for (int i = 0; i < choices.size(); i++) {
                //获取选项列表
                QuestionChoice questionChoice = choices.get(i);

                //如果存在添加新选项则需要设置question_id
                questionChoice.setQuestionId(question.getId());
                questionChoice.setId(null);
                questionChoice.setUpdateTime(null);
                questionChoice.setCreateTime(null);
                //为选项设置排序
                questionChoice.setSort(i);
                //添加新的选项
                questionChoiceMapper.insert(questionChoice);
                //遍历选项获取答案
                if (questionChoice.getIsCorrect()) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
            }
            String answer = sb.toString();
            //为questionAnswer对象赋值答案内容
            questionAnswer.setAnswer(answer);
        }

        //设置答案
        questionAnswerMapper.updateById(questionAnswer);

    }

    /**
     * 删除题目
     *
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteQuestion(Long id) {
        //判断试卷中是否已经关联改题目
        LambdaQueryWrapper<PaperQuestion> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PaperQuestion::getQuestionId, id);
        Long count = paperQuestionMapper.selectCount(lambdaQueryWrapper);
        if (count > 0) {
            throw new RuntimeException("该题目已被试卷关联%s次,删除失败".formatted(count));
        }
        //获取该题目对象
        Question question = getById(id);
        //删除主表数据
        boolean removed = removeById(id);
        if (!removed) {
            throw new RuntimeException("删除失败");
        }
        //判断是不是选择题
        if ("CHOICE".equals(question.getType())) {
            LambdaQueryWrapper<QuestionChoice> questionChoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
            questionChoiceLambdaQueryWrapper.eq(QuestionChoice::getQuestionId, question.getId());
            questionChoiceMapper.delete(questionChoiceLambdaQueryWrapper);
        }
        //删除答案表
        LambdaQueryWrapper<QuestionAnswer> questionAnswerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        questionAnswerLambdaQueryWrapper.eq(QuestionAnswer::getQuestionId, question.getId());
        questionAnswerMapper.delete(questionAnswerLambdaQueryWrapper);

    }

    /**
     * 获取热门题目
     *
     * @param size
     * @return
     */
    @Override
    public List<Question> getPopularQuestions(Integer size) {

        //创建一个热题集合
        List<Question> popularQuestionList = new ArrayList<>();

        //从redis中获取前6个数据
        Set<Object> objects = redisUtils.zReverseRange(CacheConstants.POPULAR_QUESTIONS_KEY, 0, size - 1);

        //以获取的id集合
        List<Long> ids = new ArrayList<>();
        if (objects != null && objects.size() > 0) {
            //将redis中获取的数据转换为id
            List<Long> redisIds = objects.stream().map(t -> Long.valueOf(t.toString())).collect(Collectors.toList());
            ids.addAll(redisIds);
            for (Long id : ids) {
                Question question = getById(id);
                if (question != null) {
                    popularQuestionList.add(question);
                }
            }
        }

        //判断是否够6个数据
        int diff = size - ids.size();
        //如果不够6个可以从数据库获取
        if (diff > 0) {
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            //获取题目不在redis的获取的id中，按照更新时间以及创建时间倒序排序
            lambdaQueryWrapper.notIn(ids.size() > 0, Question::getId, ids);
            lambdaQueryWrapper.orderByDesc(Question::getUpdateTime);
            lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            lambdaQueryWrapper.last("limit " + diff);
            //查询数据
            List<Question> list = list(lambdaQueryWrapper);
            //添加到热题集合中
            if (list != null && list.size() > 0) {
                popularQuestionList.addAll(list);
            }
        }
        //返回结果
        return popularQuestionList;
    }

    //    进行热点题目缓存,访问一次则为数据分值增加1
    private void incrementQuestion(Long id) {
        Double score = redisUtils.zIncrementScore(CacheConstants.POPULAR_QUESTIONS_KEY, id, 1);
        log.info("为id：{}的题目增加分值完毕，增加后的分值为：{}", id, score);
    }

    private void fillQuestionChoiceAndAnswer(List<Question> questionList) {
        //获取questionList的所有id
        List<Long> ids = questionList.stream().map(Question::getId).collect(Collectors.toList());

        //判断ids为否为空
        if (!ids.isEmpty()) {

            //获取所有选项
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.in(QuestionChoice::getQuestionId, ids);
            List<QuestionChoice> questionChoiceList = questionChoiceMapper.selectList(lambdaQueryWrapper);
            //获取所有答案
            LambdaQueryWrapper<QuestionAnswer> lambdaQueryWrapper2 = new LambdaQueryWrapper<>();
            lambdaQueryWrapper2.in(QuestionAnswer::getQuestionId, ids);
            List<QuestionAnswer> questionAnswerList = questionAnswerMapper.selectList(lambdaQueryWrapper2);

            //将所有选项转为map
            Map<Long, List<QuestionChoice>> choiceMap =
                    questionChoiceList.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
            //将所有答案转为map
            Map<Long, QuestionAnswer> answerMap =
                    questionAnswerList.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, a -> a));

            //为题目集合赋值选项和答案
            questionList.forEach(question -> {
                //设置答案
                question.setAnswer(answerMap.get(question.getId()));
                //判断类似是否为选择题
                if (("CHOICE").equals(question.getType())) {
                    List<QuestionChoice> qChoiceList = choiceMap.get(question.getId());
                    //根据sort排序答案
                    qChoiceList.sort(Comparator.comparing(QuestionChoice::getSort));
                    //设置选项
                    question.setChoices(qChoiceList);
                }
            });
        }
    }


    /**
     * excel批量导入题目
     *
     * @param file
     * @return
     */
    @Override
    public String importFormExcel(MultipartFile file) throws IOException {
        //调用方法转换为QuestionImportVo列表
        List<QuestionImportVo> questionImportVos = previewData(file);
        //保存对象
        int successCount = importBatchQuestions(questionImportVos);
        log.info("批量导入excel文件的题目数量:{},成功数量:{},失败数量:{}", questionImportVos.size(), successCount, questionImportVos.size() - successCount);
        return "批量导入excel文件的题目数量:%s,成功数量:%s,失败数量:%s".formatted(questionImportVos.size(), successCount, questionImportVos.size() - successCount);
    }

    /**
     * 预览数据
     *
     * @param file
     * @return
     */
    @Override
    public List<QuestionImportVo> previewData(MultipartFile file) throws IOException {
        //判断文件是否为空
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("上传的软件为空！");
        }
        //判断是否为xls或xlsx格式文件
        if (!file.getOriginalFilename().endsWith(".xls") && !file.getOriginalFilename().endsWith(".xlsx")) {
            throw new RuntimeException("上传文件格式应为xls或xlsx");
        }

        //调用工具类解析
        List<QuestionImportVo> questionImportVos = ExcelUtil.parseExcel(file);
        //返回结果
        return questionImportVos;
    }


    /**
     * 批量导入题目
     *
     * @param questions
     * @return
     */
    @Transactional
    @Override
    public int importBatchQuestions(List<QuestionImportVo> questions) {
        //校验是否为空
        if (questions == null || questions.isEmpty()) {
            throw new RuntimeException("您所导入的题目为空");
        }
        int successCount = 0;
        //进行题目保存
        for (int i = 0; i < questions.size(); i++) {
            try {
                //vo转换为question
                Question question = convertVoToQuestion(questions.get(i));
                //题目保存
                createQuestion(question);
                //成功计数
                successCount++;
            } catch (Exception e) {
                log.warn("{}题目导入失败", questions.get(i).getTitle());
            }
        }
        return successCount;
    }

    private Question convertVoToQuestion(QuestionImportVo questionImportVo) {
        Question question = new Question();
        BeanUtils.copyProperties(questionImportVo, question);

        //判断是否为选择题
        if ("CHOICE".equals(question.getType())) {
            //获取所有导入题目的选项
            List<QuestionImportVo.ChoiceImportDto> choiceImportChoice = questionImportVo.getChoices();

            //为要保存的题目对象设置选项各个参数
            List<QuestionChoice> questionChoices = new ArrayList<>();
            for (QuestionImportVo.ChoiceImportDto choiceImport : choiceImportChoice) {
                QuestionChoice questionChoice = new QuestionChoice();
                questionChoice.setSort(choiceImport.getSort());
                questionChoice.setIsCorrect(choiceImport.getIsCorrect());
                questionChoice.setContent(choiceImport.getContent());
                questionChoices.add(questionChoice);
            }

            //设置选项
            question.setChoices(questionChoices);
        }

        //设置答案
        QuestionAnswer questionAnswer = new QuestionAnswer();

        //如果是判断需要转为大写
        if ("JUDGE".equals(question.getType())) {
            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
        } else {
            questionAnswer.setAnswer(questionImportVo.getAnswer());
        }
        //设置关键字
        questionAnswer.setKeywords(questionImportVo.getKeywords());
        //为问题设置答案对象
        question.setAnswer(questionAnswer);
        log.info("question:{}", question);
        //返回结果
        return question;

    }
}